package org.finesys.common.es.pool;

import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.finesys.common.es.properties.MyElasticsearchProperties;
import org.finesys.common.es.util.ESUtil;
import org.jasypt.encryption.StringEncryptor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 异步池化管理对象工厂
 */
@RequiredArgsConstructor
@Slf4j
public class ElasticsearchAsyncClientFactory extends BasePooledObjectFactory<ElasticsearchAsyncClient> {

    private final MyElasticsearchProperties elasticsearchProperties;
    private final JsonpMapper jsonpMapper;
    private final StringEncryptor stringEncryptor;


    /**
     * 激活ElasticsearchAsyncClient对象。
     *
     * <p>此方法调用父类的activateObject方法，用于激活传入的PooledObject<ElasticsearchAsyncClient>对象。
     * 在对象池管理中，激活对象通常意味着将其从非活动状态转换为活动状态，以便可以从中借用。
     *
     * @param p 包含ElasticsearchAsyncClient对象的PooledObject。
     * @throws Exception 如果在激活过程中发生异常，则抛出该异常。
     * @see GenericObjectPool#activateObject(PooledObject)
     */
    @Override
    public void activateObject(PooledObject<ElasticsearchAsyncClient> p) throws Exception {
        if (log.isInfoEnabled()) {
            log.info("激活ElasticsearchAsyncClient对象");
        }
        super.activateObject(p);
    }

    /**
     * 销毁ElasticsearchAsyncClient对象。
     *
     * <p>此方法用于销毁传入的PooledObject<ElasticsearchAsyncClient>对象。
     * 首先检查传入的PooledObject是否为null，如果是，则直接返回。
     * 如果不为null，则获取其中的ElasticsearchAsyncClient对象，并调用其shutdown方法关闭客户端连接。
     * 最后，调用父类的destroyObject方法完成销毁操作。
     *
     * @param p 包含ElasticsearchAsyncClient对象的PooledObject。
     * @throws Exception 如果在销毁过程中发生异常，则抛出该异常。
     * @see GenericObjectPool#destroyObject(PooledObject)
     */
    @Override
    public void destroyObject(PooledObject<ElasticsearchAsyncClient> p) throws Exception {
        if (p == null) {
            return;
        }
        if (log.isInfoEnabled()) {
            log.info("销毁ElasticsearchAsyncClient对象");
        }
        ElasticsearchAsyncClient client = p.getObject();
        if (client != null) {
            client.shutdown();
        }
        super.destroyObject(p);
    }


    /**
     * 使ElasticsearchAsyncClient对象进入非活动状态。
     *
     * <p>此方法用于将传入的PooledObject<ElasticsearchAsyncClient>对象标记为非活动状态。
     * 在对象池管理中，非活动状态的对象不会被借出，但会被保留在池中以便将来使用。
     * 此方法首先调用父类的passivateObject方法，以执行必要的非活动状态处理逻辑。
     *
     * @param p 包含ElasticsearchAsyncClient对象的PooledObject。
     * @throws Exception 如果在非活动状态处理过程中发生异常，则抛出该异常。
     * @see GenericObjectPool#passivateObject(PooledObject)
     */
    @Override
    public void passivateObject(PooledObject<ElasticsearchAsyncClient> p) throws Exception {
        if (log.isInfoEnabled()) {
            log.info("使ElasticsearchAsyncClient对象进入非活动状态");
        }
        super.passivateObject(p);
    }

    /**
     * 验证ElasticsearchAsyncClient对象是否有效。
     *
     * <p>此方法通过调用ElasticsearchAsyncClient的ping方法，检查客户端是否能够成功连接到Elasticsearch服务。
     * 如果ping操作成功返回，则认为对象有效；否则认为对象无效。
     *
     * @param p 包含ElasticsearchAsyncClient对象的PooledObject。
     * @return 如果对象有效，则返回true；否则返回false。
     */
    @Override
    public boolean validateObject(PooledObject<ElasticsearchAsyncClient> p) {
        if (p == null) {
            return false;
        }
        ElasticsearchAsyncClient client = p.getObject();
        if (client == null) {
            return false;
        }
        CompletableFuture<BooleanResponse> responseCompletableFuture = client.ping();
        try {
            return responseCompletableFuture.get().value();
        } catch (InterruptedException | ExecutionException e) {
            log.error("验证ElasticsearchAsyncClient对象时发生异常", e);
            return false;
        }
    }


    /**
     * 创建一个ElasticsearchAsyncClient实例。
     *
     * <p>此方法通过调用ESUtil的restClientTransport方法，根据提供的elasticsearchProperties（Elasticsearch配置属性）、
     * jsonpMapper（JSON处理器）和stringEncryptor（字符串加密器）来创建一个RestClientTransport实例。
     * 然后，使用这个RestClientTransport实例来创建一个新的ElasticsearchAsyncClient实例，并将其返回。
     *
     * @return 创建好的ElasticsearchAsyncClient实例。
     * @throws Exception 如果在创建过程中发生任何异常，则抛出此异常。
     */
    @Override
    public ElasticsearchAsyncClient create() throws Exception {
        RestClientTransport restClientTransport = ESUtil.restClientTransport(elasticsearchProperties, jsonpMapper, stringEncryptor);
        return new ElasticsearchAsyncClient(restClientTransport);
    }

    /**
     * 将ElasticsearchAsyncClient对象包装为PooledObject。
     *
     * <p>此方法将传入的ElasticsearchAsyncClient对象包装成一个DefaultPooledObject实例，
     * 以便将其放入对象池中进行管理。
     *
     * @param elasticsearchAsyncClient 需要被包装的ElasticsearchAsyncClient对象。
     * @return 包装后的PooledObject<ElasticsearchAsyncClient>实例。
     */
    @Override
    public PooledObject<ElasticsearchAsyncClient> wrap(ElasticsearchAsyncClient elasticsearchAsyncClient) {
        return new DefaultPooledObject<>(elasticsearchAsyncClient);
    }
}
